<?php

namespace common\components\autoLoad;

use mdm\admin\components\Configs;
use mdm\admin\components\Helper;
use mdm\admin\models\AuthItem;
use mdm\admin\models\Menu;
use mdm\admin\models\Route;
use yii\rbac\Item;

/**
 * Class Auth
 * @property string $id
 * @property array $permissions
 * @property array $menus
 * @property array $blackList
 * @package modules
 */
abstract class Auth
{

    /**
     * @var string
     */
    public $id;

    /**
     * @var array
     */
    public $permissions = [];

    /**
     * @var array
     */
    public $menus = [];

    /**
     * @var array
     */
    public $blackList = [];

    /**
     * Auth constructor.
     * @param $id
     */
    public function __construct($id)
    {
        $this->id = $id;
    }

    /**
     * filterRoute
     * @param array $routes
     * @return array
     */
    protected function filterRoute($routes = [])
    {
        $routes = array_values($routes);
        foreach ($routes as $index => $route){
            if(strpos($route,'*') || in_array($route,$this->blackList))
                unset($routes[$index]);
        }
        return $routes;
    }

    /**
     * addRoute
     */
    protected function addRoute()
    {
        $routeModel = new Route();
        $model = new Route();
        $model->addNew($this->filterRoute($routeModel->getAppRoutes($this->id)));
    }

    /**
     * addPermission
     */
    protected function addPermission()
    {
        foreach ($this->permissions as $name => $items){
            $model = new AuthItem(null);
            $model->type = Item::TYPE_PERMISSION;
            $model->name = $name;
            if ($model->save()) {
                $model->addChildren($items);
            }
        }
    }

    /**
     * addMenu
     */
    protected function addMenu()
    {
        $this->recursionAddMenuData($this->menus);
    }

    /**
     * recursionMenuData
     * @param array $menus
     * @param int $parent
     */
    protected function recursionAddMenuData($menus=[],$parent=null)
    {
        foreach ($menus as $menu){
            $model = new Menu;
            $model->load($menu,'');
            $model->parent = $parent;
            $model->save();
            if(!empty($menu['children'])){
                $this->recursionAddMenuData($menu['children'],$model->id);
            }
        }
    }

    protected function removeRoute()
    {
        $routeModel = new Route();
        $model = new Route();
        $model->remove($this->filterRoute($routeModel->getAppRoutes($this->id)));
    }

    /**
     * removePermission
     */
    protected function removePermission()
    {
        foreach ($this->permissions as $name => $items){
            $auth = Configs::authManager();
            /** @var AuthItem $model */
            $item = $auth->getPermission($name);
            if($item){
                $model = new AuthItem($item);
                $auth->remove($model->item);
            }
        }
    }

    /**
     * removeMenu
     */
    protected function removeMenu()
    {
        $this->recursionRemoveMenuData($this->menus);
    }

    /**
     * recursionRemoveMenuData
     * @param array $menus
     */
    protected function recursionRemoveMenuData($menus=[])
    {
        foreach ($menus as $menu){
            $model = Menu::find()->where(['name'=>$menu['name']])->andFilterWhere(['route'=>$menu['route']??null])->one();
            if(!empty($model)){
                $model->delete();
            }
            if(!empty($menu['children'])){
                $this->recursionRemoveMenuData($menu['children']);
            }
        }
    }


    public function beforeAdd()
    {
        return true;
    }

    public function afterAdd()
    {
        return true;
    }

    /**
     * @inheritDoc
     */
    public function add()
    {
        if(!$this->beforeAdd()){
            Log::error(get_called_class().' beforeAdd is false.');
            return false;
        }
        $this->remove();
        $this->addRoute();
        $this->addPermission();
        $this->addMenu();
        Helper::invalidate();
        return $this->afterAdd();
    }

    public function beforeRemove()
    {
        return true;
    }

    public function afterRemove()
    {
        return true;
    }

    /**
     * @inheritDoc
     */
    public function remove()
    {
        if(!$this->beforeRemove()){
            Log::error(get_called_class().' beforeRemove is false.');
            return false;
        }

        $this->removeMenu();
        $this->removePermission();
        $this->removeRoute();
        Helper::invalidate();

        return $this->afterRemove();
    }

}